//
//  UIImage+Extensions.swift
//  myhub
//
//  Created by x on 2024/5/14.
//

import UIKit
import CoreGraphics

public protocol XExtensionWarapper {
    associatedtype T
    var x: T {get}
    static var X: T.Type {get}
}

public struct Wrapper<Base> {
    public let base: Base
    init(_ base: Base) {
        self.base = base
    }
}

public extension XExtensionWarapper {
    var x: Wrapper<Self> {
        get { Wrapper(self) }
        set {}
    }
    
    static var X: Wrapper<Self>.Type {
        get { Wrapper<Self>.self }
        set {}
    }
}
extension UIImage: XExtensionWarapper {
    public enum ImageTargetMode {
        /// 与原图宽高比一致，高度会根据`targetSize`计算
        case fill
        /// 根据`targetSize`拉伸/缩放
        case fit
        /// 如果`targetSize`的比例与原图不一样则居中显示
        case center
    }
}

public extension Wrapper where Base: UIImage {
    
    var width: CGFloat { base.size.width }
    var height: CGFloat { base.size.height }
    
    /// 修复转向
    /// 系统照片默认比例3:4，照片横向
    func fixOrientation() -> UIImage {
        if base.imageOrientation == .up {
            return base
        }
        
        var transform = CGAffineTransform.identity
        
        switch base.imageOrientation {
        case .down, .downMirrored:
            transform = CGAffineTransform(translationX: base.size.width, y: base.size.height)
            transform = transform.rotated(by: .pi)
        case .left, .leftMirrored:
            transform = CGAffineTransform(translationX: base.size.width, y: 0)
            transform = transform.rotated(by: CGFloat.pi / 2)
        case .right, .rightMirrored:
            transform = CGAffineTransform(translationX: 0, y: base.size.height)
            transform = transform.rotated(by: -CGFloat.pi / 2)
        default:
            break
        }
        
        switch base.imageOrientation {
        case .upMirrored, .downMirrored:
            transform = transform.translatedBy(x: base.size.width, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)
        case .leftMirrored, .rightMirrored:
            transform = transform.translatedBy(x: base.size.height, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)
        default:
            break
        }
        
        guard let cgImage = base.cgImage, let colorSpace = cgImage.colorSpace else {
            return base
        }
        let context = CGContext(
            data: nil,
            width: Int(base.size.width),
            height: Int(base.size.height),
            bitsPerComponent: cgImage.bitsPerComponent,
            bytesPerRow: 0,
            space: colorSpace,
            bitmapInfo: cgImage.bitmapInfo.rawValue
        )
        context?.concatenate(transform)
        switch base.imageOrientation {
        case .left, .leftMirrored, .right, .rightMirrored:
            context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: base.size.height, height: base.size.width))
        default:
            context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: base.size.width, height: base.size.height))
        }
        
        guard let newCgImage = context?.makeImage() else {
            return base
        }
        return UIImage(cgImage: newCgImage)
    }
    /**
     *  将视图绘制到图片上
     *  绘制使用drawHierarchy，会按照所有图层效果绘制
     */
    func addViewToImage(customView: UIView, screenPosition: CGPoint) -> UIImage? {
        // 创建一个新的图形上下文，大小与原始图片相同
        UIGraphicsBeginImageContextWithOptions(base.size, false, base.scale)
        
        // 绘制原始图片
        base.draw(in: CGRect(x: 0, y: 0, width: base.size.width, height: base.size.height))
        
        // 计算缩放后的大小
        let scaledSize = customView.bounds.size.applying(customView.transform)
        
        // 将自定义视图绘制到一个新的图形上下文中，并转换成图片
        let renderer = UIGraphicsImageRenderer(size: scaledSize)
        let imageContext = renderer.image { _ in
            customView.drawHierarchy(in: CGRect(origin: .zero, size: scaledSize), afterScreenUpdates: true)
        }
        
        let watermarkRect = CGRect(origin: screenPosition, size: base.size)
        
        // 将水印图片绘制到图片上
        imageContext.draw(in: watermarkRect)
        
        // 获取绘制后的图片
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        
        // 结束图形上下文
        UIGraphicsEndImageContext()
        
        return resultImage
    }
    
    /// 给图片加border
    func addImageBorder(with borderImage: UIImage) -> UIImage? {
        guard let cgImage = base.cgImage, let borderCGImage = borderImage.cgImage else {
            return nil
        }
        
        // 确保边框图片与原始图片的尺寸一致
        let size = CGSize(width: cgImage.width, height: cgImage.height)
        
        // 创建位图上下文
        guard let colorSpace = cgImage.colorSpace else { return nil }
        guard let context = CGContext(
            data: nil,
            width: Int(size.width),
            height: Int(size.height),
            bitsPerComponent: cgImage.bitsPerComponent,
            bytesPerRow: 0,
            space: colorSpace,
            bitmapInfo: cgImage.bitmapInfo.rawValue
        ) else {
            return nil
        }
        
        // 绘制原始图片
        context.draw(cgImage, in: CGRect(origin: .zero, size: size))
        
        // 绘制边框图片
        context.draw(borderCGImage, in: CGRect(origin: .zero, size: size))
        
        // 从上下文中获取合成图片
        guard let combinedCGImage = context.makeImage() else {
            return nil
        }
        // 转换为 UIImage
        return UIImage(cgImage: combinedCGImage, scale: base.scale, orientation: base.imageOrientation)
    }
    
    func scaleSuitableSize() -> UIImage? {
        var imageSize = base.size
        while imageSize.width * imageSize.height > 3 * 1000 * 1000 {
            imageSize.width *= 0.5
            imageSize.height *= 0.5
        }
        return self.scaleToFillSize(size: imageSize)
    }
    
    /// 缩放到指定大小
    func scaleToFillSize(size: CGSize, mode: Base.ImageTargetMode = .fill, scale: CGFloat = 0) -> UIImage? {
        if __CGSizeEqualToSize(base.size, size) {
            return base
        }
        let rect: CGRect
        let rendererSize: CGSize
        if mode == .fill {
            let isEqualRatio = size.width / size.height == width / height
            if isEqualRatio {
                rendererSize = size
                rect = CGRect(origin: .zero, size: size)
            }else {
                let scale = size.width / width
                var scaleHeight = scale * height
                var scaleWidth = size.width
                if scaleHeight < size.height {
                    scaleWidth = size.height / scaleHeight * size.width
                    scaleHeight = size.height
                }
                rendererSize = .init(width: scaleWidth, height: scaleHeight)
                rect = .init(origin: .zero, size: rendererSize)
            }
        }else {
            rendererSize = size
            if mode == .fit {
                rect = CGRect(origin: .zero, size: size)
            }else {
                var x: CGFloat = 0
                var y: CGFloat = 0
                let scale = size.width / width
                var scaleWidth = size.width
                var scaleHeight = scale * height
                if scaleHeight < size.height {
                    scaleWidth = size.height / scaleHeight * scaleWidth
                    scaleHeight = size.height
                }
                if scaleWidth < size.width {
                    scaleHeight = size.width / scaleWidth * scaleHeight
                    scaleWidth = size.width
                }
                x = -(scaleWidth - size.width) / 2
                y = -(scaleHeight - size.height) / 2
                rect = CGRect(
                    x: x,
                    y: y,
                    width: scaleWidth,
                    height: scaleHeight
                )
            }
        }
        let format = UIGraphicsImageRendererFormat()
        format.opaque = false
        format.scale = scale == 0 ? base.scale : scale
        let renderer = UIGraphicsImageRenderer(size: rendererSize, format: format)
        let image = renderer.image { context in
            base.draw(in: rect)
        }
        return image
    }
    func scaleImage(toScale: CGFloat) -> UIImage? {
        if toScale == 1 {
            return base
        }
        let format = UIGraphicsImageRendererFormat()
        format.opaque = false
        format.scale = base.scale
        let renderer = UIGraphicsImageRenderer(size: CGSize(width: width * toScale, height: height * toScale), format: format)
        let image = renderer.image { context in
            base.draw(in: CGRect(x: 0, y: 0, width: width * toScale, height: height * toScale))
        }
        return image
    }
    
    /// 根据颜色生成照片
    /// UIGraphicsImageRenderer 是线程安全的，可在子线程中调用，而传统的 UIGraphics 方法必须在主线程中使用。
    static func image(
        for color: UIColor?,
        havingSize: CGSize,
        radius: CGFloat = 0,
        scale: CGFloat = UIScreen.main.scale
    ) -> UIImage? {
        if let color = color {
            let rect: CGRect
            if havingSize.equalTo(CGSize.zero) {
                rect = CGRect(x: 0, y: 0, width: 1, height: 1)
            }else {
                rect = CGRect(x: 0, y: 0, width: havingSize.width, height: havingSize.height)
            }
            let format = UIGraphicsImageRendererFormat()
            format.opaque = false
            format.scale = scale
            let renderer = UIGraphicsImageRenderer(size: rect.size, format: format)
            let image = renderer.image { context in
                let cgContext = context.cgContext
                cgContext.setFillColor(color.cgColor)
                let path = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath
                cgContext.addPath(path)
                cgContext.fillPath()
            }
            return image
        }
        return nil
    }
    /// 基于已有图片进行操作，颜色填充绘制。
    func filled(withColor color: UIColor, scale: CGFloat = UIScreen.main.scale) -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        format.opaque = false
        format.scale = scale
        let renderer = UIGraphicsImageRenderer(size: base.size, format: format)
        let newImage = renderer.image { context in
            color.setFill()
            let cgContext = context.cgContext
            cgContext.translateBy(x: 0, y: base.size.height)
            cgContext.scaleBy(x: 1.0, y: -1.0)
            cgContext.setBlendMode(CGBlendMode.normal)
            let rect = CGRect(x: 0, y: 0, width: base.size.width, height: base.size.height)
            
            guard let mask = base.cgImage else { return }
            cgContext.clip(to: rect, mask: mask)
            cgContext.fill(rect)
        }
        
//        UIGraphicsBeginImageContextWithOptions(base.size, false, base.scale)
//        color.setFill()
//        
//        guard let context = UIGraphicsGetCurrentContext() else { return base }
//        context.translateBy(x: 0, y: base.size.height)
//        context.scaleBy(x: 1.0, y: -1.0)
//        context.setBlendMode(CGBlendMode.normal)
//        
//        let rect = CGRect(x: 0, y: 0, width: base.size.width, height: base.size.height)
//        
//        guard let mask = base.cgImage else { return base }
//        context.clip(to: rect, mask: mask)
//        context.fill(rect)
//        
//        let newImage = UIGraphicsGetImageFromCurrentImageContext()!
//        UIGraphicsEndImageContext()
        return newImage
    }
    
    /// 是否是正常方向的图片
    func normalizedImage() -> UIImage? {
        if base.imageOrientation == .up {
            return base
        }
        return repaintImage()
    }
    func repaintImage() -> UIImage? {
        let format = UIGraphicsImageRendererFormat()
        format.opaque = false
        format.scale = base.scale
        let renderer = UIGraphicsImageRenderer(size: base.size, format: format)
        let image = renderer.image { context in
            base.draw(in: CGRect(x: 0, y: 0, width: base.size.width, height: base.size.height))
        }
        return image
    }
    /// 图片裁圆: 按中心
    /// 使用的是 UIGraphicsImageRenderer
    func roundCropping() -> UIImage? {
        let renderer = UIGraphicsImageRenderer(size: base.size)
        let newImage = renderer.image { context in
            let width = min(base.size.width, base.size.height)
            let rect = CGRect(x: (base.size.width - width) * 0.5, y: (base.size.height - width) * 0.5, width: width, height: width)
            let path = UIBezierPath(ovalIn: rect)
            path.addClip()
            base.draw(at: .zero)
        }
        return newImage
    }
    
    /// 照片按照 【宽高比例】裁剪
    func cropImage(aspectRatio: CGFloat) -> UIImage? {
        // 使用 CGImage 进行裁剪
        guard let cgImage = base.cgImage else { return nil }
        
        let originalWidth = CGFloat(cgImage.width)
        let originalHeight = CGFloat(cgImage.height)
        let originalAspectRatio = originalWidth / originalHeight
        
        var cropRect: CGRect
        
        if originalAspectRatio > aspectRatio {
            // 原图宽高比大于目标宽高比，裁剪宽度
            let newWidth = originalHeight * aspectRatio
            let xOffset = (originalWidth - newWidth) / 2
            cropRect = CGRect(x: xOffset, y: 0, width: newWidth, height: originalHeight)
        } else {
            // 原图宽高比小于或等于目标宽高比，裁剪高度
            let newHeight = originalWidth / aspectRatio
            let yOffset = (originalHeight - newHeight) / 2
            cropRect = CGRect(x: 0, y: yOffset, width: originalWidth, height: newHeight)
        }
        
        guard let croppedCgImage = cgImage.cropping(to: cropRect) else { return nil }
        return UIImage(cgImage: croppedCgImage)
    }
    /// 指定裁剪区域 (cropRect)，从图片中截取任意矩形区域。
    /// 根据图片相对于视图的缩放比例计算裁剪区域
    func cropImage(toRect cropRect: CGRect, viewWidth: CGFloat, viewHeight: CGFloat) -> UIImage? {
        if cropRect.isEmpty {
            return base
        }
        // 计算图片相对于显示视图的缩放比例
        let imageViewScale = max(base.size.width / viewWidth, base.size.height / viewHeight)
        // 缩放cropRect以处理比屏幕显示尺寸更大的图像
        let cropZone = CGRect(
            x: cropRect.origin.x * imageViewScale,
            y: cropRect.origin.y * imageViewScale,
            width: cropRect.size.width * imageViewScale,
            height: cropRect.size.height * imageViewScale
        )
        // 如果 cropZone 超出了图片范围，可能会导致裁剪失败，在裁剪前进行边界修正
        let adjustedCropZone = cropZone.intersection(CGRect(origin: .zero, size: base.size))

        guard !adjustedCropZone.isEmpty, let cutImageRef = base.cgImage?.cropping(to: adjustedCropZone) else {
            return nil
        }

        let croppedImage = UIImage(cgImage: cutImageRef, scale: base.scale, orientation: base.imageOrientation)
        return croppedImage
    }
    
    /// 旋转图片
    func rotation(to orientation: UIImage.Orientation) -> UIImage? {
        if let cgImage = base.cgImage {
            func swapWidthAndHeight(_ toRect: CGRect) -> CGRect {
                var rect = toRect
                let swap = rect.width
                rect.size.width = rect.height
                rect.size.height = swap
                return rect
            }
            var rect = CGRect(x: 0, y: 0, width: cgImage.width, height: cgImage.height)
            while rect.width * rect.height > 3 * 1000 * 1000 {
                rect.size.width = rect.width * 0.5
                rect.size.height = rect.height * 0.5
            }
            var trans: CGAffineTransform
            var bnds = rect
            switch orientation {
            case .up:
                return base
            case .upMirrored:
                trans = .init(translationX: rect.width, y: 0)
                trans = trans.scaledBy(x: -1, y: 1)
            case .down:
                trans = .init(translationX: rect.width, y: rect.height)
                trans = trans.rotated(by: CGFloat.pi)
            case .downMirrored:
                trans = .init(translationX: 0, y: rect.height)
                trans = trans.scaledBy(x: 1, y: -1)
            case .left:
                bnds = swapWidthAndHeight(bnds)
                trans = .init(translationX: 0, y: rect.width)
                trans = trans.rotated(by: 3 * CGFloat.pi * 0.5)
            case .leftMirrored:
                bnds = swapWidthAndHeight(bnds)
                trans = .init(translationX: rect.height, y: rect.width)
                trans = trans.scaledBy(x: -1, y: 1)
                trans = trans.rotated(by: 3 * CGFloat.pi * 0.5)
            case .right:
                bnds = swapWidthAndHeight(bnds)
                trans = .init(translationX: rect.height, y: 0)
                trans = trans.rotated(by: CGFloat.pi * 0.5)
            case .rightMirrored:
                bnds = swapWidthAndHeight(bnds)
                trans = .init(scaleX: -1, y: 1)
                trans = trans.rotated(by: CGFloat.pi * 0.5)
            default:
                return base
            }
            let renderer = UIGraphicsImageRenderer(size: bnds.size)
            let newImage = renderer.image { context in
                let cgContext = context.cgContext
                switch orientation {
                case .left, .leftMirrored, .right, .rightMirrored:
                    cgContext.scaleBy(x: -1, y: 1)
                    cgContext.translateBy(x: -rect.height, y: 0)
                default:
                    cgContext.scaleBy(x: 1, y: -1)
                    cgContext.translateBy(x: 0, y: -rect.height)
                }
                cgContext.concatenate(trans)
                cgContext.draw(cgImage, in: rect)
            }
            return newImage
        }
        return nil
    }
    /// 按角度旋转方向：90度
    func rotation(angle: Int, isHorizontal: Bool) -> UIImage? {
        switch angle {
        case 0, 360, -360:
            if isHorizontal {
                return rotation(to: .upMirrored)
            }
        case 90, -270:
            if !isHorizontal {
                return rotation(to: .right)
            }else {
                return rotation(to: .rightMirrored)
            }
        case 180, -180:
            if !isHorizontal {
                return rotation(to: .down)
            }else {
                return rotation(to: .downMirrored)
            }
        case 270, -90:
            if !isHorizontal {
                return rotation(to: .left)
            }else {
                return rotation(to: .leftMirrored)
            }
        default:
            break
        }
        return base
    }
    /// 合并图片
    func merge(_ image: UIImage, origin: CGPoint, scale: CGFloat = UIScreen.main.scale) -> UIImage? {
        let format = UIGraphicsImageRendererFormat()
        format.opaque = false
        format.scale = scale
        let renderer = UIGraphicsImageRenderer(size: base.size, format: format)
        let mergeImage = renderer.image { context in
            image.draw(in: CGRect(origin: origin, size: base.size))
        }
        return mergeImage
    }
    /// 合并多张图片
    func merge(images: [UIImage], scale: CGFloat = UIScreen.main.scale) -> UIImage? {
        if images.isEmpty {
            return base
        }
        let format = UIGraphicsImageRendererFormat()
        format.opaque = false
        format.scale = scale
        let renderer = UIGraphicsImageRenderer(size: base.size, format: format)
        let mergeImage = renderer.image { context in
            base.draw(in: CGRect(origin: .zero, size: base.size))
            for image in images {
                image.draw(in: CGRect(origin: .zero, size: base.size))
            }
        }
        return mergeImage
    }
    
    static func merge(images: [UIImage], scale: CGFloat? = nil) -> UIImage? {
        if images.isEmpty {
            return nil
        }
        if images.count == 1 {
            return images.first
        }
        var _scale: CGFloat = 1
        if let scale = scale {
            _scale = scale
        }else {
            if !Thread.isMainThread {
                DispatchQueue.main.sync {
                    _scale = UIScreen.main.scale
                }
            }else {
                _scale = UIScreen.main.scale
            }
        }
        let format = UIGraphicsImageRendererFormat()
        format.opaque = false
        format.scale = _scale
        let renderer = UIGraphicsImageRenderer(size: images.first!.size, format: format)
        let mergeImage = renderer.image { context in
            for image in images {
                image.draw(in: CGRect(origin: .zero, size: image.size))
            }
        }
        return mergeImage
    }
}
